# type: ignore
"""init

Revision ID: f2ad2214f165
Revises: 
Create Date: 2025-10-27 01:22:03.429848

"""

import warnings
from typing import TYPE_CHECKING

import sqlalchemy as sa
from alembic import op
from advanced_alchemy.types import EncryptedString, EncryptedText, GUID, ORA_JSONB, DateTimeUTC
from sqlalchemy import Text  # noqa: F401

if TYPE_CHECKING:
    from collections.abc import Sequence

__all__ = ["downgrade", "upgrade", "schema_upgrades", "schema_downgrades", "data_upgrades", "data_downgrades"]

sa.GUID = GUID
sa.DateTimeUTC = DateTimeUTC
sa.ORA_JSONB = ORA_JSONB
sa.EncryptedString = EncryptedString
sa.EncryptedText = EncryptedText

# revision identifiers, used by Alembic.
revision = 'f2ad2214f165'
down_revision = None
branch_labels = None
depends_on = None


def upgrade() -> None:
    with warnings.catch_warnings():
        warnings.filterwarnings("ignore", category=UserWarning)
        with op.get_context().autocommit_block():
            schema_upgrades()
            data_upgrades()

def downgrade() -> None:
    with warnings.catch_warnings():
        warnings.filterwarnings("ignore", category=UserWarning)
        with op.get_context().autocommit_block():
            data_downgrades()
            schema_downgrades()

def schema_upgrades() -> None:
    """schema upgrade migrations go here."""
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('sys_dept',
    sa.Column('id', sa.BigInteger().with_variant(sa.Integer(), 'sqlite'), nullable=False),
    sa.Column('dept_name', sa.String(length=20), nullable=False, comment='部门名称'),
    sa.Column('parent_id', sa.BigInteger(), nullable=True, comment='父级ID'),
    sa.Column('ancestors', sa.String(length=255), nullable=False, comment='祖级列表'),
    sa.Column('status', sa.String(length=1), nullable=False, comment='帐号状态（1正常 2停用）'),
    sa.Column('leader', sa.String(length=20), nullable=True, comment='部门领导'),
    sa.Column('phone', sa.String(length=11), nullable=True, comment='电话'),
    sa.Column('order_num', sa.Integer(), nullable=False, comment='排序'),
    sa.Column('remark', sa.String(length=500), nullable=True, comment='备注'),
    sa.Column('created_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.Column('updated_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.ForeignKeyConstraint(['parent_id'], ['sys_dept.id'], name=op.f('fk_sys_dept_parent_id_sys_dept'), ondelete='SET NULL'),
    sa.PrimaryKeyConstraint('id', name=op.f('pk_sys_dept'))
    )
    with op.batch_alter_table('sys_dept', schema=None) as batch_op:
        batch_op.create_index('ix_sys_dept_parent_id', ['parent_id'], unique=False)
        batch_op.create_index('ix_sys_dept_status', ['status'], unique=False)

    op.create_table('sys_dict_type',
    sa.Column('id', sa.BigInteger().with_variant(sa.Integer(), 'sqlite'), nullable=False),
    sa.Column('dict_name', sa.String(length=100), nullable=True, comment='字典名称'),
    sa.Column('dict_type', sa.String(length=100), nullable=True, comment='字典类型'),
    sa.Column('status', sa.String(length=1), nullable=True, comment='状态（1正常 2停用）'),
    sa.Column('remark', sa.String(length=522), nullable=True, comment='备注'),
    sa.Column('created_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.Column('updated_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.PrimaryKeyConstraint('id', name=op.f('pk_sys_dict_type'))
    )
    op.create_table('sys_login_record',
    sa.Column('id', sa.BigInteger().with_variant(sa.Integer(), 'sqlite'), nullable=False),
    sa.Column('username', sa.String(length=50), nullable=True, comment='用户名'),
    sa.Column('ipaddr', sa.String(length=50), nullable=True, comment='登录IP地址'),
    sa.Column('continent', sa.String(length=50), nullable=True, comment='大洲'),
    sa.Column('country', sa.String(length=50), nullable=True, comment='国家'),
    sa.Column('province', sa.String(length=50), nullable=True, comment='省份'),
    sa.Column('city', sa.String(length=50), nullable=True, comment='城市'),
    sa.Column('district', sa.String(length=50), nullable=True, comment='区县'),
    sa.Column('isp', sa.String(length=50), nullable=True, comment='运营商'),
    sa.Column('area_code', sa.String(length=50), nullable=True, comment='区号'),
    sa.Column('longitude', sa.String(length=50), nullable=True, comment='经度'),
    sa.Column('latitude', sa.String(length=50), nullable=True, comment='纬度'),
    sa.Column('browser', sa.String(length=50), nullable=True, comment='浏览器'),
    sa.Column('os', sa.String(length=50), nullable=True, comment='操作系统'),
    sa.Column('agent', sa.String(length=500), nullable=True, comment='代理'),
    sa.Column('login_type', sa.String(length=32), nullable=True, comment='登录类型'),
    sa.Column('params', sa.String(length=255), nullable=True, comment='参数'),
    sa.Column('status', sa.String(length=1), nullable=False, comment='状态0:成功，2：异常'),
    sa.Column('message', sa.String(length=50), nullable=True, comment='响应信息'),
    sa.Column('created_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.Column('updated_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.PrimaryKeyConstraint('id', name=op.f('pk_sys_login_record'))
    )
    op.create_table('sys_menu',
    sa.Column('id', sa.BigInteger().with_variant(sa.Integer(), 'sqlite'), nullable=False),
    sa.Column('parent_id', sa.BigInteger(), nullable=True, comment='父菜单ID'),
    sa.Column('menu_type', sa.String(length=1), nullable=False, comment='菜单类型; 1:目录，2:菜单，3: 按钮'),
    sa.Column('permissions', sa.String(length=100), nullable=True, comment='权限标识'),
    sa.Column('menu_name', sa.String(length=64), nullable=False, comment='菜单名称'),
    sa.Column('route_name', sa.String(length=64), nullable=True, comment='路由名称'),
    sa.Column('route_path', sa.String(length=64), nullable=True, comment='路由路径'),
    sa.Column('component', sa.String(length=64), nullable=True, comment='组件路径'),
    sa.Column('order', sa.Integer(), nullable=False, comment='排序'),
    sa.Column('i18n_key', sa.String(length=64), nullable=True, comment='国际化Key'),
    sa.Column('icon', sa.String(length=64), nullable=True, comment='图标'),
    sa.Column('icon_type', sa.String(length=64), nullable=True, comment='图标类型'),
    sa.Column('path_param', sa.String(length=200), nullable=True, comment='路径参数'),
    sa.Column('route_param', sa.String(length=200), nullable=True, comment='路由参数, List[dict]'),
    sa.Column('href', sa.String(length=200), nullable=True, comment='外链'),
    sa.Column('multiple_tab', sa.Boolean(), nullable=True, comment='是否支持多页签'),
    sa.Column('keep_alive', sa.Boolean(), nullable=False, comment='是否缓存'),
    sa.Column('hide_in_menu', sa.Boolean(), nullable=False, comment='是否在菜单隐藏'),
    sa.Column('active_menu', sa.String(length=100), nullable=True, comment='隐藏的路由需要激活的菜单'),
    sa.Column('fixed_index_in_tab', sa.Integer(), nullable=True, comment='固定在页签的序号'),
    sa.Column('redirect', sa.String(length=200), nullable=True, comment='重定向路径'),
    sa.Column('props', sa.Boolean(), nullable=True, comment='是否为首路由'),
    sa.Column('constant', sa.Boolean(), nullable=True, comment='是否为公共路由'),
    sa.Column('status', sa.String(length=1), nullable=False, comment='菜单状态1正常，2禁用'),
    sa.Column('created_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.Column('updated_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.ForeignKeyConstraint(['parent_id'], ['sys_menu.id'], name=op.f('fk_sys_menu_parent_id_sys_menu'), ondelete='SET NULL'),
    sa.PrimaryKeyConstraint('id', name=op.f('pk_sys_menu'))
    )
    with op.batch_alter_table('sys_menu', schema=None) as batch_op:
        batch_op.create_index('ix_sys_menu_menu_type', ['menu_type'], unique=False)
        batch_op.create_index('ix_sys_menu_parent_id', ['parent_id'], unique=False)
        batch_op.create_index('ix_sys_menu_permissions', ['permissions'], unique=False)
        batch_op.create_index('ix_sys_menu_route_name', ['route_name'], unique=False)
        batch_op.create_index('ix_sys_menu_route_path', ['route_path'], unique=False)
        batch_op.create_index('ix_sys_menu_status', ['status'], unique=False)

    op.create_table('sys_notice',
    sa.Column('id', sa.BigInteger().with_variant(sa.Integer(), 'sqlite'), nullable=False),
    sa.Column('notice_title', sa.String(length=50), nullable=False, comment='公告标题'),
    sa.Column('notice_type', sa.String(length=1), nullable=False, comment='公告类型（1通知 2公告）'),
    sa.Column('notice_content', sa.LargeBinary(), nullable=False, comment='公告内容'),
    sa.Column('status', sa.String(length=1), nullable=False, comment='公告状态（1正常 2关闭）'),
    sa.Column('remark', sa.String(length=255), nullable=True, comment='备注'),
    sa.Column('created_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.Column('updated_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.PrimaryKeyConstraint('id', name=op.f('pk_sys_notice'))
    )
    op.create_table('sys_operation_record',
    sa.Column('id', sa.BigInteger().with_variant(sa.Integer(), 'sqlite'), nullable=False),
    sa.Column('username', sa.String(length=100), nullable=True, comment='用户名'),
    sa.Column('client_ip', sa.String(length=100), nullable=True, comment='客户端IP'),
    sa.Column('request_api', sa.String(length=255), nullable=True, comment='请求API'),
    sa.Column('method', sa.String(length=10), nullable=True, comment='请求方式'),
    sa.Column('api_path', sa.String(length=255), nullable=True, comment='请求路径'),
    sa.Column('status_code', sa.Integer(), nullable=True, comment='状态码'),
    sa.Column('params', sa.Text(), nullable=True, comment='请求参数'),
    sa.Column('cost_time', sa.Numeric(precision=10, scale=4), nullable=False, comment='耗时'),
    sa.Column('summary', sa.String(length=255), nullable=True, comment='操作类型'),
    sa.Column('description', sa.String(length=255), nullable=True, comment='操作描述'),
    sa.Column('tags', sa.String(length=255), nullable=True, comment='标签'),
    sa.Column('system', sa.String(length=255), nullable=True, comment='操作系统'),
    sa.Column('browser', sa.String(length=255), nullable=True, comment='浏览器'),
    sa.Column('route_name', sa.String(length=255), nullable=True, comment='路由名称'),
    sa.Column('content_length', sa.Integer(), nullable=True, comment='响应长度'),
    sa.Column('request_id', sa.String(length=255), nullable=True, comment='请求ID'),
    sa.Column('created_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.Column('updated_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.PrimaryKeyConstraint('id', name=op.f('pk_sys_operation_record'))
    )
    op.create_table('sys_post',
    sa.Column('id', sa.BigInteger().with_variant(sa.Integer(), 'sqlite'), nullable=False),
    sa.Column('post_code', sa.String(length=20), nullable=False, comment='岗位编码'),
    sa.Column('post_name', sa.String(length=20), nullable=False, comment='岗位名称'),
    sa.Column('remark', sa.String(length=500), nullable=True, comment='备注'),
    sa.Column('post_sort', sa.Integer(), nullable=False, comment='显示顺序'),
    sa.Column('status', sa.String(length=1), nullable=False, comment='状态（1正常 2停用）'),
    sa.Column('created_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.Column('updated_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.PrimaryKeyConstraint('id', name=op.f('pk_sys_post'))
    )
    with op.batch_alter_table('sys_post', schema=None) as batch_op:
        batch_op.create_index('ix_sys_post_post_code', ['post_code'], unique=False)
        batch_op.create_index('ix_sys_post_status', ['status'], unique=False)

    op.create_table('sys_resource',
    sa.Column('id', sa.BigInteger().with_variant(sa.Integer(), 'sqlite'), nullable=False),
    sa.Column('filename', sa.String(length=64), nullable=False, comment='资源源名称'),
    sa.Column('url', sa.String(length=255), nullable=False, comment='资源源URL'),
    sa.Column('file_type', sa.String(length=64), nullable=False, comment='资源类型'),
    sa.Column('created_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.Column('updated_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.PrimaryKeyConstraint('id', name=op.f('pk_sys_resource'))
    )
    op.create_table('sys_role',
    sa.Column('id', sa.BigInteger().with_variant(sa.Integer(), 'sqlite'), nullable=False),
    sa.Column('role_name', sa.String(length=30), nullable=False, comment='角色名称'),
    sa.Column('role_key', sa.String(length=100), nullable=False, comment='角色权限字符串'),
    sa.Column('home_key', sa.String(length=100), nullable=False, comment='首页key'),
    sa.Column('role_sort', sa.Integer(), nullable=False, comment='显示顺序'),
    sa.Column('data_scope', sa.Integer(), nullable=False, comment='数据范围（0 仅本人数据权限 1 本部门数据权限 2 本部门及以下数据权限 3 自定义数据权限 4 全部数据权限）'),
    sa.Column('menu_check_strictly', sa.Integer(), nullable=False, comment='菜单树选择项是否关联显示'),
    sa.Column('dept_check_strictly', sa.Integer(), nullable=False, comment='部门树选择项是否关联显示'),
    sa.Column('status', sa.String(length=1), nullable=False, comment='角色状态（1正常 2停用）'),
    sa.Column('remark', sa.String(length=500), nullable=True, comment='备注'),
    sa.Column('created_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.Column('updated_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.PrimaryKeyConstraint('id', name=op.f('pk_sys_role'))
    )
    with op.batch_alter_table('sys_role', schema=None) as batch_op:
        batch_op.create_index('ix_sys_role_role_key', ['role_key'], unique=False)
        batch_op.create_index('ix_sys_role_status', ['status'], unique=False)

    op.create_table('sys_task',
    sa.Column('id', sa.BigInteger().with_variant(sa.Integer(), 'sqlite'), nullable=False),
    sa.Column('name', sa.String(length=50), nullable=False, comment='任务名称'),
    sa.Column('group', sa.String(length=50), nullable=True, comment="任务分组'memory' or 'redis'"),
    sa.Column('job_class', sa.String(length=255), nullable=False, comment='任务执行实体类'),
    sa.Column('job_params', sa.String(length=255), nullable=False, comment='任务执行实体类参数, dict 类型参数, 入参方式为：**kwargs'),
    sa.Column('exec_strategy', sa.String(length=50), nullable=False, comment='执行策略'),
    sa.Column('expression', sa.String(length=50), nullable=True, comment='执行表达式, 一次任务可以不填'),
    sa.Column('is_active', sa.Boolean(), nullable=False, comment='状态是否正常'),
    sa.Column('remark', sa.String(length=255), nullable=True, comment='描述'),
    sa.Column('start_datetime', sa.DateTime(), nullable=True, comment='任务开始时间'),
    sa.Column('end_datetime', sa.DateTime(), nullable=True, comment='任务结束时间'),
    sa.Column('created_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.Column('updated_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.PrimaryKeyConstraint('id', name=op.f('pk_sys_task'))
    )
    op.create_table('sys_dict_data',
    sa.Column('id', sa.BigInteger().with_variant(sa.Integer(), 'sqlite'), nullable=False),
    sa.Column('dict_sort', sa.Integer(), nullable=True, comment='字典排序'),
    sa.Column('dict_label', sa.String(length=100), nullable=True, comment='字典标签'),
    sa.Column('dict_value', sa.String(length=100), nullable=True, comment='字典键值'),
    sa.Column('dict_type_id', sa.BigInteger(), nullable=False, comment='字典类型ID'),
    sa.Column('is_default', sa.String(length=1), nullable=True, comment='是否默认（Y是 N否）'),
    sa.Column('status', sa.String(length=1), nullable=True, comment='状态（1正常 2停用）'),
    sa.Column('remark', sa.String(length=500), nullable=True, comment='备注'),
    sa.Column('created_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.Column('updated_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.ForeignKeyConstraint(['dict_type_id'], ['sys_dict_type.id'], name=op.f('fk_sys_dict_data_dict_type_id_sys_dict_type'), ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id', name=op.f('pk_sys_dict_data'))
    )
    op.create_table('sys_role_dept',
    sa.Column('role_id', sa.BigInteger(), nullable=False),
    sa.Column('dept_id', sa.BigInteger(), nullable=False),
    sa.ForeignKeyConstraint(['dept_id'], ['sys_dept.id'], name=op.f('fk_sys_role_dept_dept_id_sys_dept'), ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['role_id'], ['sys_role.id'], name=op.f('fk_sys_role_dept_role_id_sys_role'), ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('role_id', 'dept_id', name=op.f('pk_sys_role_dept'))
    )
    op.create_table('sys_role_menu',
    sa.Column('role_id', sa.BigInteger(), nullable=False),
    sa.Column('menu_id', sa.BigInteger(), nullable=False),
    sa.ForeignKeyConstraint(['menu_id'], ['sys_menu.id'], name=op.f('fk_sys_role_menu_menu_id_sys_menu'), ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['role_id'], ['sys_role.id'], name=op.f('fk_sys_role_menu_role_id_sys_role'), ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('role_id', 'menu_id', name=op.f('pk_sys_role_menu'))
    )
    op.create_table('sys_user',
    sa.Column('id', sa.BigInteger().with_variant(sa.Integer(), 'sqlite'), nullable=False),
    sa.Column('username', sa.String(length=20), nullable=False, comment='用户名'),
    sa.Column('nickname', sa.String(length=20), nullable=True, comment='昵称'),
    sa.Column('avatar', sa.String(length=255), nullable=True, comment='头像'),
    sa.Column('email', sa.String(length=64), nullable=True, comment='邮箱'),
    sa.Column('phone', sa.String(length=11), nullable=True, comment='电话'),
    sa.Column('password', sa.String(length=128), nullable=False, comment='密码'),
    sa.Column('status', sa.String(length=1), nullable=False, comment='帐号状态（1正常 2停用）'),
    sa.Column('is_super', sa.String(length=1), nullable=False, comment='帐号状态（0普通 1管理员）'),
    sa.Column('sex', sa.String(length=1), nullable=False, comment='用户性别（0女 1男 2未知）'),
    sa.Column('login_ip', sa.String(length=30), nullable=True, comment='最后登录时间'),
    sa.Column('remark', sa.String(length=255), nullable=True, comment='描述'),
    sa.Column('dept_id', sa.BigInteger(), nullable=True, comment='部门ID'),
    sa.Column('created_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.Column('updated_at', sa.DateTimeUTC(timezone=True), nullable=False),
    sa.ForeignKeyConstraint(['dept_id'], ['sys_dept.id'], name=op.f('fk_sys_user_dept_id_sys_dept'), ondelete='SET NULL'),
    sa.PrimaryKeyConstraint('id', name=op.f('pk_sys_user')),
    sa.UniqueConstraint('username', name='uq_sys_user_username_new')
    )
    with op.batch_alter_table('sys_user', schema=None) as batch_op:
        batch_op.create_index('ix_sys_user_dept_id', ['dept_id'], unique=False)
        batch_op.create_index('ix_sys_user_email', ['email'], unique=False)
        batch_op.create_index('ix_sys_user_is_super', ['is_super'], unique=False)
        batch_op.create_index('ix_sys_user_phone', ['phone'], unique=False)
        batch_op.create_index('ix_sys_user_status', ['status'], unique=False)

    op.create_table('sys_user_post',
    sa.Column('user_id', sa.BigInteger(), nullable=False),
    sa.Column('post_id', sa.BigInteger(), nullable=False),
    sa.ForeignKeyConstraint(['post_id'], ['sys_post.id'], name=op.f('fk_sys_user_post_post_id_sys_post'), ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['user_id'], ['sys_user.id'], name=op.f('fk_sys_user_post_user_id_sys_user'), ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('user_id', 'post_id', name=op.f('pk_sys_user_post'))
    )
    op.create_table('sys_user_role',
    sa.Column('user_id', sa.BigInteger(), nullable=False),
    sa.Column('role_id', sa.BigInteger(), nullable=False),
    sa.ForeignKeyConstraint(['role_id'], ['sys_role.id'], name=op.f('fk_sys_user_role_role_id_sys_role'), ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['user_id'], ['sys_user.id'], name=op.f('fk_sys_user_role_user_id_sys_user'), ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('user_id', 'role_id', name=op.f('pk_sys_user_role'))
    )
    # ### end Alembic commands ###

def schema_downgrades() -> None:
    """schema downgrade migrations go here."""
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('sys_user_role')
    op.drop_table('sys_user_post')
    with op.batch_alter_table('sys_user', schema=None) as batch_op:
        batch_op.drop_index('ix_sys_user_status')
        batch_op.drop_index('ix_sys_user_phone')
        batch_op.drop_index('ix_sys_user_is_super')
        batch_op.drop_index('ix_sys_user_email')
        batch_op.drop_index('ix_sys_user_dept_id')

    op.drop_table('sys_user')
    op.drop_table('sys_role_menu')
    op.drop_table('sys_role_dept')
    op.drop_table('sys_dict_data')
    op.drop_table('sys_task')
    with op.batch_alter_table('sys_role', schema=None) as batch_op:
        batch_op.drop_index('ix_sys_role_status')
        batch_op.drop_index('ix_sys_role_role_key')

    op.drop_table('sys_role')
    op.drop_table('sys_resource')
    with op.batch_alter_table('sys_post', schema=None) as batch_op:
        batch_op.drop_index('ix_sys_post_status')
        batch_op.drop_index('ix_sys_post_post_code')

    op.drop_table('sys_post')
    op.drop_table('sys_operation_record')
    op.drop_table('sys_notice')
    with op.batch_alter_table('sys_menu', schema=None) as batch_op:
        batch_op.drop_index('ix_sys_menu_status')
        batch_op.drop_index('ix_sys_menu_route_path')
        batch_op.drop_index('ix_sys_menu_route_name')
        batch_op.drop_index('ix_sys_menu_permissions')
        batch_op.drop_index('ix_sys_menu_parent_id')
        batch_op.drop_index('ix_sys_menu_menu_type')

    op.drop_table('sys_menu')
    op.drop_table('sys_login_record')
    op.drop_table('sys_dict_type')
    with op.batch_alter_table('sys_dept', schema=None) as batch_op:
        batch_op.drop_index('ix_sys_dept_status')
        batch_op.drop_index('ix_sys_dept_parent_id')

    op.drop_table('sys_dept')
    # ### end Alembic commands ###

def data_upgrades() -> None:
    """Add any optional data upgrade migrations here!"""

def data_downgrades() -> None:
    """Add any optional data downgrade migrations here!"""
